#include "ShipManager.hpp"

#include "LuaFile.h"
#include "LuaTable.h"

// These are the default values for when a ship template gets loaded 
// from the file. If a ship template does not specify a value for an
// attribute, these are what get used.
#define DEFAULT_SHIP_MASS				1500.0f
#define DEFAULT_SHIP_LINEAR_DRAG		0.300f
#define DEFAULT_SHIP_ANGULAR_DRAG		5.0f
#define DEFAULT_SHIP_FORWARD_THRUST		4800000.0f
#define DEFAULT_SHIP_REVERSE_FRACTION	0.5f
#define DEFAULT_SHIP_STEERING_THRUST	10000.0f

// Constructor.
ShipManager::ShipManager()
{
	m_pPhysWorld = 0;
	m_pSceneMgr = 0;
}

// Does exactly the same thing as the setSceneManager function but is called
// this so that it becomes more apparent that this MUST be done when the 
// ShipManager is created.
void ShipManager::createShipManager(SceneManager* i_pSceneMgr, OgreNewt::World* i_pPhysWorld)
{
	m_pSceneMgr = i_pSceneMgr;
	m_pPhysWorld = i_pPhysWorld;
}

// Used to set the scene manager.
void ShipManager::setSceneManager(SceneManager* i_pSceneMgr)
{
	m_pSceneMgr = i_pSceneMgr;
}

// Parse a ship file. This will go through any files that have been marked as
// ship files and parse them, filling out the template table used for new ships.
void ShipManager::parseShipFile(std::string i_sFileName)
{
	// Open the shiparch.lua file and get the ship table from it.
	LuaFile File(i_sFileName, "r");
	File.Execute();
	LuaTable ShipArchTable = File.GetRoot();

	// Go through the ship table and create a template.
	std::vector<int> keys;
	if (ShipArchTable.GetKeys(keys))
	{
		// This loops through each key (ship). 
		for (std::vector<int>::iterator it = keys.begin(); it != keys.end(); ++it)
		{
			// Load the current ship template.
			LuaTable CurShipTable = ShipArchTable.SubTable(*it);
			ShipTemplate* pCurShip = new ShipTemplate();

			// Use the values from the file to fill in the ship template.
			pCurShip->sShipName = CurShipTable.GetString("name", "");
			pCurShip->sMeshName = CurShipTable.GetString("mesh", "");
			pCurShip->fMass = CurShipTable.GetFloat("mass", DEFAULT_SHIP_MASS);
			pCurShip->fLinearDrag = CurShipTable.GetFloat("linear_drag", DEFAULT_SHIP_LINEAR_DRAG);
			pCurShip->fForwardThrust = CurShipTable.GetFloat("forward_thrust", DEFAULT_SHIP_FORWARD_THRUST);
			pCurShip->fReverseFraction = CurShipTable.GetFloat("reverse_fraction", DEFAULT_SHIP_REVERSE_FRACTION);

			pCurShip->fAngularDrag.x = CurShipTable.SubTable("angular_drag").GetFloat(1, DEFAULT_SHIP_ANGULAR_DRAG);
			pCurShip->fAngularDrag.y = CurShipTable.SubTable("angular_drag").GetFloat(2, DEFAULT_SHIP_ANGULAR_DRAG);
			pCurShip->fAngularDrag.z = CurShipTable.SubTable("angular_drag").GetFloat(3, DEFAULT_SHIP_ANGULAR_DRAG);			
			pCurShip->fSteeringThrust.x = CurShipTable.SubTable("steering_thrust").GetFloat(1, DEFAULT_SHIP_STEERING_THRUST);
			pCurShip->fSteeringThrust.y = CurShipTable.SubTable("steering_thrust").GetFloat(2, DEFAULT_SHIP_STEERING_THRUST);
			pCurShip->fSteeringThrust.z = CurShipTable.SubTable("steering_thrust").GetFloat(3, DEFAULT_SHIP_STEERING_THRUST);

			// Add this newly completed ship template to the list of templates.
			// But first make sure it has a valid meshname and ship name. If it
			// doesn't, this is a pretty useless template.
			if (pCurShip->sMeshName != "" && pCurShip->sShipName != "")
				m_ShipTemplateList.insert(ShipTemplatePair(pCurShip->sShipName, pCurShip));
		}
	}
}

// Creates a new ship object and adds it to the std::list.
void ShipManager::spawnNewShip(std::string i_sShipTemplateName, ShipInput* i_pShipInput, std::string i_sShipName, Vector3 i_InitalLocation, Quaternion i_InitialOrientation)
{
	std::string sFinalShipName;

	// Get the ShipTemplate that we are asked to spawn. 
	ShipTemplate* pRequestedShipTemplate = getShipTemplateByName(i_sShipTemplateName);
	
	// Make sure that this that it actually found one. 
	if (pRequestedShipTemplate == NULL)
	{
		Ogre::LogManager::getSingleton().logMessage("[ShipManager] Could not create ship due invalid ship template: " + sFinalShipName);
		return;
	}

	// DO NOT CALL THE SHIP THE DEFAULT NAME. If no name is given, then we will
	// just give it a new name based off of the model name.
	if (i_sShipName == "NoName")
		sFinalShipName = GenerateShipName(pRequestedShipTemplate->sMeshName);
	else
		sFinalShipName = i_sShipName;

	// Create the new ship and add it to the std::map of ships. Do one final last 
	// minute check and be sure the scene node name does not collide. If it does,
	// then handle it nicely.

	// If we end up with a duplicated shipname somehow, then don't create the new ship.
	// Instead, drop an error into the log.
	if (m_ShipList.count(sFinalShipName) > 0)
		Ogre::LogManager::getSingleton().logMessage("[ShipManager] Could not create ship due to duplicate name: " + sFinalShipName);

	// The shipname is not a duplicate, create the new ship.
	else
	{
		ShipObject* pNewShip = new ShipObject();
		pNewShip->spawnNewShip(m_pSceneMgr, sFinalShipName, pRequestedShipTemplate, m_pPhysWorld, i_pShipInput, i_InitalLocation, i_InitialOrientation);
		m_ShipList.insert(ShipListPair(pNewShip->getShipName(), pNewShip));
	}
}

// Function used to generate a ship name. This gets called when creating a ship with
// no specified name.
std::string ShipManager::GenerateShipName(std::string i_sModelName)
{
	// Get rid of the .mesh (or really whatever the extension is)
	int nNumOfShips = m_ShipList.size();
	size_t posOfExtension = i_sModelName.rfind('.');
	std::string sNewShipName = i_sModelName.substr(0, posOfExtension);

	// Create a new string of the form SHIPNAME_6;
	stringstream sShipNameStream;
	sShipNameStream << "cNS_AG_" << sNewShipName << "_" << nNumOfShips;

	// Return the final ship name.
	return sShipNameStream.str();
}

// Updates all the ships that have been created.
void ShipManager::updateShipManager(double timeSinceLastFrame)
{
	// Iterate through all the ships in the std::list and call their update functions.
	std::map<std::string, ShipObject*>::iterator itr;
	for (itr=m_ShipList.begin(); itr != m_ShipList.end(); itr++)
	{
		(*itr).second->updateShip(timeSinceLastFrame);
	}
}

// Deletes a ship from the ship std::list. Effectively erasing it from existence.
void ShipManager::deleteShip(std::string i_sShipName)
{
	// Iterate through all the ships in the std::list and delete when we find a 
	// name match.
	std::map<std::string, ShipObject*>::iterator itr;
	for (itr=m_ShipList.begin(); itr != m_ShipList.end(); itr++)
	{
		if ((*itr).second->getShipName() == i_sShipName)
		{
			m_ShipList.erase(itr);
		}
	}
}

// Get a ship from the ship std::list. Returns a pointer to that ship.
ShipObject* ShipManager::getShipPointerByName(std::string i_sShipName)
{
	// Use the power of maps to return a pointer to the ShipObject requested.
	if (m_ShipList.count(i_sShipName) > 0)
		return m_ShipList[i_sShipName];

	// If it doens't exist, return a null pointer. Whoever called this will
	// have to deal with it.
	return 0;
}

// Function that returns the ship template with the appropriate name. If the
// template does not exist, it will return NULL, which should always be checked
// against whenever this gets used.
ShipTemplate* ShipManager::getShipTemplateByName(const std::string i_sTemplateName)
{
	return m_ShipTemplateList[i_sTemplateName];
}

// Delete all the memory that was used up by the ShipObjects in the list.
void ShipManager::eraseShipListContents()
{
	std::map<std::string, ShipObject*>::iterator itr;
	for (itr=m_ShipList.begin(); itr!=m_ShipList.end(); itr++)
	{
		delete itr->second;
		itr->second = 0;
	}
}

// Delete all the memory that was used up by the Templates in the map.
void ShipManager::eraseShipTemplateContents()
{
	std::map<std::string, ShipTemplate*>::iterator itr;
	for (itr=m_ShipTemplateList.begin(); itr!=m_ShipTemplateList.end(); itr++)
	{
		delete itr->second;
		itr->second = 0;
	}
	m_ShipTemplateList.clear();
}

// Destructor.
ShipManager::~ShipManager()
{
	eraseShipTemplateContents();
	eraseShipListContents();
}